package com.usf.jprovider.factory;

import java.io.IOException;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.usf.jprovider.object.JObject;
import com.usf.jprovider.object.JObjectNode;
import com.usf.jprovider.object.env.Environment;
import com.usf.jprovider.object.type.JString;
import com.usf.jprovider.schema.model.AbstractModel;
import com.usf.jprovider.schema.model.jconfiguration.JConfiguration;
import com.usf.jprovider.schema.model.jmodel.JModel;
import com.usf.jprovider.tools.XMLTools;

public final class JFactory {

	public static <T extends AbstractModel>T loadModel(String path, Class<T> clazz) throws JAXBException {
		return XMLTools.load(path, clazz);	
	}
	public static JModel loadJModel(String path) throws JAXBException {
		return loadModel(path, JModel.class);	
	}
	public static JConfiguration loadConfiguration(String path) throws JAXBException {
		return loadModel(path, JConfiguration.class);	
	}
	public static void save(Object object, String path) throws IOException, JAXBException{
		XMLTools.save(object, path);
	}
	
	public static JObject createAndInject(JModel model, Environment env)  throws Exception {
		JObject result = createData(model, env);
		return injectData(result, model, env);
	}
	public static void createAndInject(JModel model, Environment env, String outputXML) throws Exception {
		JObject result = createData(model, env);
		result = injectData(result, model, env);
		saveData(result, model, env, outputXML);
	}

	public static boolean check(String file, JModel model, Environment env) throws Exception {
		JObject obj = loadObject(file);
		return check(obj, model, env);
	}

	public static JObject inject(JObject obj, JModel model, Environment env) throws Exception {
		if(check(obj, model, env)) return injectData(obj, model, env);
		throw new NullPointerException("Can't persist " + model.getName() + " data model");
	}
	public static void inject(JObject obj, JModel model, Environment env, String outputXML) throws Exception {
		JObject result = inject(obj, model, env);
		saveData(result, model, env, outputXML);
	}
	public static boolean check(JObject obj, JModel model, Environment env)  throws Exception {
		env.getLogger().info("Checking " + model.getName() + " data model ...");
		boolean result = model.check(env, obj);
		env.getLogger().info(model.getName() + " data model checked = " + result);
		return result;
	}
	private static JObject createData(JModel model, Environment env) throws Exception{
		env.getLogger().info("Creating " + model.getName() + " data model ...");
		JObject result = model.create(env);
		env.getLogger().info(model.getName() + " data model created");
		return result;
	} 
	private static JObject injectData(JObject obj, JModel model, Environment env) throws Exception{
		env.getLogger().info("Injecting " + model.getName() + " data model ...");
		JObject result = model.persist(env, obj);
		env.getLogger().info(model.getName() + " data model injected");
		return result;
	}
	private static void saveData(JObject obj, JModel model, Environment env, String outputXML)  throws Exception {
		env.getLogger().info("Saving " + model.getName() + " data model ...");
		XMLTools.saveDocument(toXML(obj), outputXML);
		env.getLogger().info(model.getName() + " data model saved on " + outputXML);
	}
	
	
	public static JObject loadObject(String path) throws ParserConfigurationException, SAXException, IOException{
		Document document = XMLTools.loadDocument(path);
		Node root = document.getFirstChild();
		JObject tree = new JObject(root.getNodeName());
		appendsNodes(root.getChildNodes(), tree);
		return tree;
	}
	
	public static Document toXML(JObject object) throws ParserConfigurationException{
		Document document = XMLTools.createDocument();
		Node node = document.createElement(object.getName());
		document.appendChild(node);
		appendObjects(node, object.childs());
		return document;
	}
	
	private static void appendObjects(Node node, JObjectNode[] nodes){
		for(JObjectNode item : nodes){
			Node child = node.getOwnerDocument().createElement(item.getName());
			node.appendChild(child);
			if(item.hasChilds())
				appendObjects(child, item.childs());
			else
				child.setTextContent(item.getValue().getFormatedValue());
		}
	}
	private static void appendsNodes(NodeList nodes, JObjectNode node){
		for(int i=0; i<nodes.getLength(); i++){
			Node current = nodes.item(i);
			if(current.getNodeType() != Node.TEXT_NODE){
				JObjectNode item = node.create(current.getNodeName());
				node.addChild(item);
				if(current.getChildNodes().getLength() > 1)
					appendsNodes(current.getChildNodes(), item);
				else 
					item.setValue(new JString(current.getTextContent()));
			}
		}
	}	
}
